package composer

import (
	"context"
	"sync"

	"gddgame.cc/galaxy/utils/cache"
	"gddgame.cc/galaxy/utils/def"
)

type Component interface {
	Init(Register, Builder) error
}

type Composer interface {
	SetDebug(flag bool)
	Component(component Component)
	SetLogger(logger def.Logger)
	SetBasis(basis BasisCloser)

	Builder(index int) Builder
	Register(index int) Register

	Start(ctx context.Context) error
	Stop() error
}

type composer struct {
	logger def.Logger

	debug bool

	basis BasisCloser

	instances map[string]interface{}
	executers []Executer
	listeners []Listener
	ctx       context.Context

	components []Component

	contextPool sync.Pool
}

func NewCompose(logger def.Logger) Composer {
	c := &composer{
		basis:      newLocal(logger),
		components: make([]Component, 0),
		instances:  make(map[string]interface{}),
		executers:  make([]Executer, 0),
		listeners:  make([]Listener, 0),
	}
	c.contextPool = sync.Pool{New: func() interface{} {
		context := &Context{c: c}
		context.Logger = c.logger.LoggerWrap("compose", "context")
		context.Store = &cache.Store{
			Data: make(map[string]interface{}),
		}
		return context
	}}
	c.SetLogger(logger)
	return c
}

func (c *composer) SetDebug(flag bool) {
	c.debug = flag
}

func (c *composer) Component(component Component) {
	c.components = append(c.components, component)

	c.logger.Debugf("[ Composer ] Register component: %d, %#v", len(c.components)-1, component)
}

func (c *composer) SetBasis(basis BasisCloser) {
	c.basis = basis
}

func (c *composer) SetLogger(logger def.Logger) {
	c.logger = logger
}

func (c *composer) Register(index int) Register {
	register := &register{
		index:    index,
		composer: c,
	}
	return register
}

func (c *composer) Builder(index int) Builder {
	builder := &builder{
		index:       index,
		Logger:      c.logger,
		ExpireStore: c.basis.Cache("composer"),
		Basis:       c.basis,
		composer:    c,
	}
	return builder
}

func (c *composer) Start(ctx context.Context) error {
	c.ctx = ctx
	for k, _ := range c.components {
		builder := c.Builder(k)
		register := c.Register(k)
		c.logger.Debugf("[ Composer ] Init component: %#v", k)
		if err := c.components[k].Init(register, builder); err != nil {
			return err
		}
	}
	for k, _ := range c.executers {
		c.logger.Debugf("[ Composer ] Exec executer %#v", k)
		if err := c.executers[k](); err != nil {
			return err
		}
	}
	c.logger.Debugf("[ Composer ] Listener Start")
	for k, _ := range c.listeners {
		if err := c.listeners[k].Listen(); err != nil {
			return err
		}
	}

	return nil
}

func (c *composer) Stop() error {
	c.logger.Debugf("[ Composer ] Listener Close")
	for k, _ := range c.listeners {
		if err := c.listeners[k].Close(); err != nil {
			return err
		}
	}
	for k, _ := range c.instances {
		if closer, ok := c.instances[k].(Closer); ok {
			if err := closer.Close(); err != nil {
				c.logger.Error(err)
			}
		}
	}
	c.logger.Debugf("[ Composer ] Basis Close")
	if err := c.basis.Close(); err != nil {
		return err
	}
	return nil
}
